/*
   Copyright 2008, Andres Postiglioni

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/
package com.aconcagua.context {
  import com.aconcagua.beans.Bean;
  import com.aconcagua.beans.BeanDefinitionSupport;
  import com.aconcagua.beans.Property;
  import com.aconcagua.context.binding.BindProperty;
  import com.aconcagua.context.binding.BindSetter;
  import com.aconcagua.dummy.DummyBean;

  import flexunit.framework.TestCase;

  /**
   * @author Andres Postiglioni
   */
  public class BindingContextTest extends TestCase {
    private var _context: BindingContext;

    override public function setUp(): void {
      _context = new BindingContext();
    }

    override public function tearDown():void {
      _context = null;
    }

    public function testBindProperty_SingletonBeanAsSource_ObjectAsTarget(): void {
      var value: DummyBean = new DummyBean();

      var sourceBean: Bean = new Bean();
      sourceBean.initialized(null, "source");
      sourceBean.type = DummyBean;
      sourceBean.metadata = [ new Property("propertyBean", value) ];
      _context.beans = [ sourceBean ];

      var target: DummyBean = new DummyBean();

      var bindProperty: BindProperty = new BindProperty();
      bindProperty.source = sourceBean;
      bindProperty.sourceChain = "propertyBean";
      bindProperty.target = target;
      bindProperty.targetProperty = "propertyBean";
      _context.bindings = [ bindProperty ];

      _context.init();

      var source: DummyBean = _context.getBean("source");

      //property must be binded
      assertEquals(value, source.propertyBean);
      assertEquals(source.propertyBean, target.propertyBean);

      //refresh property to ensure binding works
      value = new DummyBean();
      source.propertyBean = value;
      assertEquals(value, source.propertyBean);
      assertEquals(source.propertyBean, target.propertyBean);
    }

    public function testBindProperty_LazySingletonAsSource_ObjectAsTarget(): void {
      var value: DummyBean = new DummyBean();

      var sourceBean: Bean = new Bean();
      sourceBean.initialized(null, "source");
      sourceBean.type = DummyBean;
      sourceBean.lazy = true;
      sourceBean.scope = BeanDefinitionSupport.SCOPE_SINGLETON;
      sourceBean.metadata = [ new Property("propertyBean", value) ];
      _context.beans = [ sourceBean ];

      var target: DummyBean = new DummyBean();

      var bindProperty: BindProperty = new BindProperty();
      bindProperty.source = sourceBean;
      bindProperty.sourceChain = "propertyBean";
      bindProperty.target = target;
      bindProperty.targetProperty = "propertyBean";
      _context.bindings = [ bindProperty ];

      _context.init();

      //property must be initialized after context init
      assertEquals(value, target.propertyBean);

      var source: DummyBean = _context.getBean("source");

      //property must be binded
      assertEquals(value, source.propertyBean);
      assertEquals(source.propertyBean, target.propertyBean);

      //refresh property to ensure binding works
      value = new DummyBean();
      source.propertyBean = value;
      assertEquals(value, source.propertyBean);
      assertEquals(source.propertyBean, target.propertyBean);
    }

/*
//Binding on protoype beans is not supported.
    public function testBindProperty_PrototypeBeanAsSource_ObjectAsTarget(): void {
      var value: DummyBean = new DummyBean();

      var sourceBean: Bean = new Bean();
      sourceBean.initialized(null, "source");
      sourceBean.type = DummyBean;
      sourceBean.scope = BeanDefinitionSupport.SCOPE_PROTOTYPE;
      sourceBean.metadata = [ new Property("propertyBean", value) ];
      _context.beans = [ sourceBean ];

      var target: DummyBean = new DummyBean();

      var bindProperty: BindProperty = new BindProperty();
      bindProperty.source = sourceBean;
      bindProperty.sourceChain = "propertyBean";
      bindProperty.target = target;
      bindProperty.targetProperty = "propertyBean";
      _context.bindings = [ bindProperty ];

      _context.init();

      var source: DummyBean = _context.getBean("source");

      //property must be binded
      assertEquals(value, source.propertyBean);
      assertEquals(source.propertyBean, target.propertyBean);

      //refresh property to ensure binding works
      value = new DummyBean();
      source.propertyBean = value;
      assertEquals(value, source.propertyBean);
      assertEquals(source.propertyBean, target.propertyBean);
    }
*/

    public function testBindProperty_ObjectAsSource_BeanAsTarget(): void {
      var value: DummyBean = new DummyBean();

      var targetBean: Bean = new Bean();
      targetBean.type = DummyBean;
      targetBean.initialized(null, "target");
      _context.beans = [ targetBean ];

      var source: DummyBean = new DummyBean();
      source.propertyBean = value;

      var bindProperty: BindProperty = new BindProperty();
      bindProperty.source = source;
      bindProperty.sourceChain = "propertyBean";
      bindProperty.target = targetBean;
      bindProperty.targetProperty = "propertyBean";
      _context.bindings = [ bindProperty ];

      _context.init();

      var target: DummyBean = _context.getBean("target");

      //property must be binded
      assertEquals("source property is not initialized", value, source.propertyBean);
      assertEquals("target property is not initialized", source.propertyBean, target.propertyBean);

      //refresh property to ensure binding works
      value = new DummyBean();
      source.propertyBean = value;
      assertEquals("source property was not refreshed", value, source.propertyBean);
      assertEquals("target property was not refreshed", source.propertyBean, target.propertyBean);
    }


    public function testBindPropertyChain_ObjectAsSource_BeanAsTarget(): void {
      var value: DummyBean = new DummyBean();

      var targetBean: Bean = new Bean();
      targetBean.type = DummyBean;
      targetBean.initialized(null, "target");
      _context.beans = [ targetBean ];

      var source: DummyBean = new DummyBean();
      source.propertyBean = new DummyBean(new DummyBean());
      source.propertyBean.constructorBean.propertyBean = value;

      var bindProperty: BindProperty = new BindProperty();
      bindProperty.source = source;
      bindProperty.sourceChain = ["propertyBean", "constructorBean", "propertyBean"];
      bindProperty.target = targetBean;
      bindProperty.targetProperty = "propertyBean";
      _context.bindings = [ bindProperty ];

      _context.init();

      var target: DummyBean = _context.getBean("target");

      //property must be binded
      assertEquals("source property is not initialized", value, source.propertyBean.constructorBean.propertyBean);
      assertEquals("target property is not initialized", source.propertyBean.constructorBean.propertyBean, target.propertyBean);

      //refresh property to ensure binding works
      value = new DummyBean();
      source.propertyBean.constructorBean.propertyBean = value;

      assertEquals("source property was not refreshed after property change.", value, source.propertyBean.constructorBean.propertyBean);
      assertEquals("target property was not refreshed after property change.", source.propertyBean.constructorBean.propertyBean, target.propertyBean);

      //refresh property in chain to ensure binding works
      value = new DummyBean();
      var tmp: DummyBean = new DummyBean(new DummyBean());
      tmp.constructorBean.propertyBean = value;
      source.propertyBean = tmp;

      assertEquals("source property was not refreshed after change in chain.", value, source.propertyBean.constructorBean.propertyBean);
      assertEquals("target property was not refreshed after change in chain.", source.propertyBean.constructorBean.propertyBean, target.propertyBean);
    }

    public function testBindSetter(): void {
      var value: DummyBean = new DummyBean();
      var bindedValue: DummyBean = null;

      var sourceBean: Bean = new Bean();
      sourceBean.initialized(null, "source");
      sourceBean.type = DummyBean;
      sourceBean.metadata = [ new Property("propertyBean", value) ];
      _context.beans = [ sourceBean ];

      var target: DummyBean = new DummyBean();

      var bindSetter: BindSetter = new BindSetter();
      bindSetter.source = sourceBean;
      bindSetter.sourceChain = "propertyBean";
      bindSetter.setter = function (theValue: DummyBean): void { bindedValue = theValue; }
      _context.bindings = [ bindSetter ];

      _context.init();

      var source: DummyBean = _context.getBean("source");

      //property must be binded
      assertEquals(value, source.propertyBean);
      assertEquals(source.propertyBean, bindedValue);

      //refresh property to ensure binding works
      value = new DummyBean();
      source.propertyBean = value;
      assertEquals(value, source.propertyBean);
      assertEquals(source.propertyBean, bindedValue);
    }
  }
}